use std::cell::RefCell;
use std::rc::Rc;

use gltf::animation::util::ReadOutputs;
use gltf::buffer::Data as gltfBufferData;
use gltf::image::Data as gltfImageData;

use crate::vulkan::object::object::Object;
use crate::vulkan::object::skeletal_mesh::SkeletalMesh;
use crate::vulkan::vulkan_rhi_utils::{Vertex, VulkanRHI};

pub struct Node {
    pub name:           String,
    pub index:          usize,
    pub mesh_index:     Option<usize>,
    pub skin_index:     Option<usize>,
    pub children:       Vec<Rc<RefCell<Node>>>,
    pub parent:         Option<Rc<RefCell<Node>>>,
    pub transform:      gltf::scene::Transform,
}
impl Node {
    pub fn new(index: usize) -> Self {
        Self {
            name: String::from(""),
            index: index,
            skin_index: Some(index),
            mesh_index: None,
            children: vec![],
            parent: None,
            transform: gltf::scene::Transform::Matrix { 
                matrix: [
                    [1.0, 0.0, 0.0, 0.0],
                    [0.0, 1.0, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0],
                ] 
            }
        }
    }
}
impl Default for Node {
    fn default() -> Self {
        Self {
            name: String::from(""),
            index: 0,
            skin_index: None,
            mesh_index: None,
            children: vec![],
            parent: None,
            transform: gltf::scene::Transform::Matrix { 
                matrix: [
                    [1.0, 0.0, 0.0, 0.0],
                    [0.0, 1.0, 0.0, 0.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0],
                ] 
            }
        }
    }
}

pub struct SkinSSBO {
    pub buffer: Vec<ash::vk::Buffer>,
    pub device_memory: Vec<ash::vk::DeviceMemory>,
    pub mapped_handle: Vec<*mut [[f32; 4]; 4]>
}
pub struct Skin {
    pub name: String,
    pub joints: Vec<Rc<RefCell<Node>>>,
    pub inverse_bind_matrices: Vec<[[f32;4];4]>,
    // pub ssbo: SkinSSBO
}
pub enum ChannelOutputType {
    Translation([f32; 3]),
    Rotation([f32; 4]),
    Scale([f32; 3]),
    MorphTargetWeight(f32)
}
pub struct Channel {
    pub node: Rc<RefCell<Node>>,
    pub path: gltf::animation::Property,
    pub inputs: Vec<f32>,
    pub outputs: Vec<ChannelOutputType>,
    pub interpolation: gltf::animation::Interpolation
}
pub struct Animation {
    pub name: String,
    pub channels: Vec<Channel>,
    pub animation_runtime: f32
}
pub struct GltfInput {
    pub is_valid: bool,
    gltf_path: String,
    pub nodes: Vec<Rc<RefCell<Node>>>,
    pub skins: Vec<Skin>,
    pub animations: Vec<Animation>,
}

impl GltfInput {
    fn load_gltf_node(gltf_input: &(gltf::Document, Vec<gltfBufferData>, Vec<gltfImageData>), 
        node_list: &Vec<Rc<RefCell<Node>>>, node_document: &gltf::Node, parent_node: Option<Rc<RefCell<Node>>>) {
        // 拿到自己的Node
        let index = node_document.index();
        let mut self_node = node_list[index].borrow_mut();
        // 填充信息
        self_node.name = String::from(node_document.name().unwrap_or("None"));
        self_node.index = index;
        self_node.parent = parent_node.clone();
        self_node.transform = node_document.transform().clone();
        self_node.skin_index = match node_document.skin() {
            Some(skin_index) => Some(skin_index.index()),
            None => None
        };
        self_node.mesh_index = match node_document.mesh() {
            Some(mesh_index) => Some(mesh_index.index()),
            None => None  
        };
        // 遍历子节点
        for childnode_document in node_document.children() {
            self_node.children.push(node_list[childnode_document.index()].clone());
            Self::load_gltf_node(gltf_input, node_list, &childnode_document, Some(node_list[index].clone()));
        }
    }
    fn load_gltf_nodes(gltf_input: &(gltf::Document, Vec<gltfBufferData>, Vec<gltfImageData>)) -> Vec<Rc<RefCell<Node>>> {
        // 初始化Nodes列表，因为Node需要被引用来引用去，这样设计最好
        let mut node_list = vec![];
        for index in 0..gltf_input.0.nodes().len() {
            node_list.push(Rc::new(RefCell::new(Node::new(index))));
        }
        // 读取Scene中的根Node，根Node按照Node树迭代找Children实现整棵树Load
        for scene in gltf_input.0.scenes() {
            for node in scene.nodes() {
                Self::load_gltf_node(&gltf_input, &node_list, &node, None);
            }
        }

        return node_list;
    }
    fn load_gltf_skins(gltf_input: &(gltf::Document, Vec<gltfBufferData>, Vec<gltfImageData>), node_list: &Vec<Rc<RefCell<Node>>>) -> Vec<Skin> {
        // 初始化Skins列表
        let mut skin_list = vec![];
        for skin in gltf_input.0.skins() {
            let reader = skin.reader(|buffer| Some(&gltf_input.1[buffer.index()]));
            
            skin_list.push(Skin {
                name: String::from(skin.name().unwrap_or("None")),
                joints: skin.joints().map(|joint| node_list[joint.index()].clone()).collect(),
                inverse_bind_matrices: reader.read_inverse_bind_matrices().unwrap().map(|matrix| matrix).collect()
            })
        }

        skin_list
    }
    fn load_gltf_animations(gltf_input: &(gltf::Document, Vec<gltfBufferData>, Vec<gltfImageData>), node_list: &Vec<Rc<RefCell<Node>>>) -> Vec<Animation> {
        // 初始化Animations列表
        let mut animation_list = vec![];
        for animation in gltf_input.0.animations() {
            animation_list.push(Animation { 
                name: 
                    String::from(animation.name().unwrap_or("None")),
                animation_runtime:
                    0.0,
                channels: 
                    animation.channels().map(
                        //  这部分数据需要用到reader
                        |channel| {
                            let reader = channel.reader(|buffer| Some(&gltf_input.1[buffer.index()]));
                            
                            // K帧时间
                            let mut inputs = vec![];
                            if let Some(read_inputs) = reader.read_inputs() {
                                for input in read_inputs { inputs.push(input) }
                            }
                            // 对应的K帧属性
                            let mut outputs = vec![];
                            if let Some(read_outputs) = reader.read_outputs() {
                                match read_outputs {
                                    ReadOutputs::Translations(read_outputs) => {
                                        for output in read_outputs {
                                            outputs.push(ChannelOutputType::Translation(output));
                                        }
                                    }
                                    ReadOutputs::Rotations(read_outputs) => {
                                        for output in read_outputs.into_f32() {
                                            outputs.push(ChannelOutputType::Rotation(output));
                                        }
                                    }
                                    ReadOutputs::Scales(read_outputs) => {
                                        for output in read_outputs {
                                            outputs.push(ChannelOutputType::Scale(output));
                                        }
                                    }
                                    ReadOutputs::MorphTargetWeights(read_outputs) => {
                                        for output in read_outputs.into_f32() {
                                            outputs.push(ChannelOutputType::MorphTargetWeight(output));
                                        }
                                    }
                                }
                            }
        
                            Channel {
                                node: node_list[channel.target().node().index()].clone(),
                                path: channel.target().property(),
                                inputs: inputs,
                                outputs: outputs,
                                interpolation: channel.sampler().interpolation()
                            }
                        }
                    ).collect()
            })
        }

        animation_list
    }
    pub fn load_gltf(gltf_path: String) -> Result<Self, Box<dyn std::error::Error>> {
        // 读取文件
        let gltf_input = gltf::import(&gltf_path)?;
        // 获取Nodes列表
        let nodes = Self::load_gltf_nodes(&gltf_input);
        // 获取Skins数据
        let skins = Self::load_gltf_skins(&gltf_input, &nodes);
        // 获取Animations数据
        let animations = Self::load_gltf_animations(&gltf_input, &nodes);

        println!("[Application] Success to Load GLTF({})", gltf_path);
        Ok(Self {
            is_valid: true,
            gltf_path: gltf_path,
            nodes: nodes,
            skins: skins,
            animations: animations
        })
    }

    pub fn glm_matrix_to_gltf_matrix(&self, glm_matrix: glm::Matrix4<f32>) -> [[f32; 4]; 4] {
        glm_matrix.as_array().map(|vector| *vector.as_array())
    }
    pub fn gltf_matrix_to_glm_matrix(&self, gltf_matrix: [[f32; 4]; 4]) -> glm::Matrix4<f32>  {
        *glm::Matrix4::from_array(
            &gltf_matrix.map(|vector| glm::Vector4::new(vector[0], vector[1], vector[2], vector[3]))
        )
    }
    pub fn get_node_local_matrix(&self, node: Rc<RefCell<Node>>) -> glm::Matrix4<f32> {
        self.gltf_matrix_to_glm_matrix(node.borrow().transform.clone().matrix())
    }

    pub fn get_node_matrix(&self, node: Rc<RefCell<Node>>) -> glm::Matrix4<f32> {
        let node_borrow = node.borrow();

        let mut current_parent = node_borrow.parent.clone();
        let mut output_matrix = self.get_node_local_matrix(node.clone());

        while let Some(parent) = current_parent {
            output_matrix = 
                self.get_node_local_matrix(parent.clone()) * 
                output_matrix;
            current_parent = parent.borrow().parent.clone();
        }

        return output_matrix;
    }

    pub fn make_mesh_objects(rc_ref_self: Rc<RefCell<Self>>, vulkan_rhi: &VulkanRHI) -> Vec<Rc<RefCell<dyn Object>>> {
        let failed_return = vec![];
        let mut skeletal_meshes: Vec<Rc<RefCell<dyn Object>>> = vec![];
        let rc_self = rc_ref_self.borrow();

        if rc_self.is_valid == false {
            return failed_return
        }
        // 读取文件
        let gltf_input = gltf::import(&rc_self.gltf_path).unwrap();
        // 通过读入的信息构造Mesh
        for node in &rc_self.nodes {
            if node.borrow().mesh_index.is_none() { 
                continue;
            }
            let mesh_document = gltf_input.0.meshes().nth(node.borrow().mesh_index.unwrap()).unwrap();
            let mut vertex_buffer = vec![];
            let mut index_buffer = vec![];

            // 读入Primitive的数据，构造VertexBuffer与IndexBuffer
            for primitive in mesh_document.primitives() {
                // 读取数据
                let reader = primitive.reader(|buffer| Some(&gltf_input.1[buffer.index()]));
                let Some(position_iter) = 
                    reader.read_positions() else { println!("[SkeletalMesh Error] Failed to read position"); return failed_return};
                let Some(normal_iter) = 
                    reader.read_normals() else { println!("[SkeletalMesh Error] Failed to read normal"); return failed_return};
                let Some(uv_iter) = 
                    reader.read_tex_coords(0) else { println!("[SkeletalMesh Error] Failed to read uv"); return failed_return};
                let Some(joint_index_iter) = 
                    reader.read_joints(0) else { println!("[SkeletalMesh Error] Failed to read joint_index"); return failed_return};
                let Some(joint_weight_iter) = 
                    reader.read_weights(0) else { println!("[SkeletalMesh Error] Failed to read joint_weight"); return failed_return};

                // 重整数据类型
                let uv_iter = uv_iter.into_f32();
                let joint_index_iter = joint_index_iter.into_u16();
                let joint_weight_iter = joint_weight_iter.into_f32();

                // Fill Vertex Buffer
                for ((((position, normal), uv), joint_index), joint_weight) in position_iter.zip(normal_iter).zip(uv_iter).zip(joint_index_iter).zip(joint_weight_iter) {
                    let vertex = Vertex {
                        position: position,
                        normal: normal,
                        uv: uv,
                        color: [0.0, 0.0, 0.0],
                        joint_index: joint_index,
                        joint_weight: joint_weight
                    };
                    vertex_buffer.push(vertex);
                }

                // Fill Index Buffer
                let Some(indices_iter) = reader.read_indices() else { println!("[SkeletalMesh Error] Failed to read indices"); return failed_return};
                let indices_iter = indices_iter.into_u32();
                for index in indices_iter {
                    index_buffer.push(index);
                }
            }
            // Texture
            let rerange_image = |image: &gltfImageData| -> gltfImageData {
                let mut buffer = vec![0; (4 * image.height * image.width) as usize];
                for index in 0..image.height * image.width {
                    buffer[(index * 4 + 0) as usize] = image.pixels[(index * 3 + 0) as usize];
                    buffer[(index * 4 + 1) as usize] = image.pixels[(index * 3 + 1) as usize];
                    buffer[(index * 4 + 2) as usize] = image.pixels[(index * 3 + 2) as usize];
                    buffer[(index * 4 + 3) as usize] = 0;
                }
                gltfImageData {
                    pixels: buffer,
                    format: gltf::image::Format::R8G8B8A8,
                    width: image.width,
                    height: image.height
                }
            };
            let mut image = gltf_input.2[0].clone();
            if image.format == gltf::image::Format::R8G8B8 {
                image = rerange_image(&image);
            }

            // 构造
            skeletal_meshes.push(
                Rc::new( RefCell::new( SkeletalMesh::from_gltf(
                    vulkan_rhi, 
                    vertex_buffer, 
                    index_buffer, 
                    image, 
                    rc_self.skins.get(node.borrow().skin_index.unwrap_or(usize::MAX)),
                    node.borrow().skin_index.unwrap_or(usize::MAX),
                    rc_ref_self.clone(),
                    glm::inverse(&rc_self.get_node_matrix(node.clone()))
                ).expect("[Skeletal Mesh] Failed to Create Mesh")))
            )
        }

        skeletal_meshes
    }

    pub fn update_animation(&mut self, delta_time: f32) {
        for animation in &mut self.animations {
            animation.animation_runtime = animation.animation_runtime + delta_time;
            if animation.animation_runtime > 2.0 {
                animation.animation_runtime -= 2.0;
            }

            for channel in &animation.channels {

                for timer_index in 0..channel.inputs.len() - 1 {
                    if animation.animation_runtime > channel.inputs[timer_index] && animation.animation_runtime < channel.inputs[timer_index + 1] {
                        let coef = (animation.animation_runtime - channel.inputs[timer_index]) / (channel.inputs[timer_index + 1] - channel.inputs[timer_index]);
                        let mut node = channel.node.borrow_mut();
                        let (translation, rotation, scale) = node.transform.clone().decomposed();
                        
                        let transform = match (&channel.outputs[timer_index], &channel.outputs[timer_index + 1]) {
                            (ChannelOutputType::Translation(output), ChannelOutputType::Translation(output_next)) => {
                                gltf::scene::Transform::Decomposed { 
                                    translation: [
                                        output[0] + (output_next[0] - output[0]) * coef, 
                                        output[1] + (output_next[1] - output[1]) * coef,
                                        output[2] + (output_next[2] - output[2]) * coef], 
                                    rotation: rotation, 
                                    scale: scale
                                }
                            },
                            (ChannelOutputType::Rotation(output), ChannelOutputType::Rotation(output_next)) => {
                                gltf::scene::Transform::Decomposed { 
                                    translation: translation, 
                                    rotation: [
                                        output[0] + (output_next[0] - output[0]) * coef, 
                                        output[1] + (output_next[1] - output[1]) * coef,
                                        output[2] + (output_next[2] - output[2]) * coef,
                                        output[3] + (output_next[3] - output[3]) * coef], 
                                    scale: scale
                                }
                            },
                            (ChannelOutputType::Scale(output), ChannelOutputType::Scale(output_next)) => {
                                gltf::scene::Transform::Decomposed { 
                                    translation: translation, 
                                    rotation: rotation, 
                                    scale: [
                                        output[0] + (output_next[0] - output[0]) * coef, 
                                        output[1] + (output_next[1] - output[1]) * coef,
                                        output[2] + (output_next[2] - output[2]) * coef], 
                                }
                            },
                            (ChannelOutputType::MorphTargetWeight(_output), ChannelOutputType::MorphTargetWeight(_output_next)) => {
                                gltf::scene::Transform::Decomposed { 
                                    translation: translation.clone(), 
                                    rotation: rotation.clone(), 
                                    scale: scale.clone() 
                                }
                            },
                            _ => {
                                println!("error type");
                                gltf::scene::Transform::Decomposed { 
                                    translation: translation, 
                                    rotation: rotation, 
                                    scale: scale
                                }
                            }
                        };

                        node.transform = transform.clone();
                    }
                }
            }
        }
    }

    pub fn get_nodes(&self) -> &Vec<Rc<RefCell<Node>>> {
        &self.nodes
    }
}